package com.smarthome.common.httpwapper;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;

import org.apache.http.config.SocketConfig;
import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;


public class HttpClientConnectionPoolManager {

    private int maxTotal = 50;//设置整个连接池最大连接数 根据自己的场景决定
    private int defaultMaxPerRoute = 25; //每个路由最大连接数，一个服务端相当于1个路由，有多少个服务端(域名或者IP)就有多少个路由
    
    //http
    private HttpClientBuilder httpClientBuilder;
    private CloseableHttpClient httpClient;
    
    //https
    private HttpClientBuilder httpsClientBuilder;    
    private CloseableHttpClient httpsClient;
    private static HttpClientConnectionPoolManager poolManager = null;
    
    private HttpClientConnectionPoolingAdapter pool=null;

    private SocketConfig getCustomSocketConfig(){
        SocketConfig  socketConfig=SocketConfig.custom()
                                  .setTcpNoDelay(true)//Nagle算法试图通过减少分片的数量来节省带宽。当应用程序希望降低网络延迟并提高性能时，可以关闭Nagle算法，这样数据将会更早地发送，但会增加网络消耗
                                  .build();
        return socketConfig;
    }
    
//    private  RequestConfig getCustomRequestConfig(){
//        RequestConfig requestConfig = RequestConfig.custom()
//                                      .setSocketTimeout(120*1000) //获取数据的超时时间
//                                      .setConnectTimeout(120*1000)//建立连接超时时间
//                                      .setConnectionRequestTimeout(120*1000) //从连接管理器中获取连接超时时间
//                                      .build();
//        return requestConfig;
//    }

    private HttpClientConnectionPoolManager(int maxTotal, int defaultMaxPerRoute){
        this.maxTotal = maxTotal;
        this.defaultMaxPerRoute = defaultMaxPerRoute;
        pool=new HttpClientConnectionPoolingAdapter();
        pool.setMaxTotal(maxTotal);
        pool.setDefaultMaxPerRoute(defaultMaxPerRoute);
        pool.setDefaultSocketConfig(getCustomSocketConfig());
        buildHttpClient(pool);
    }
    
    
    private HttpClientConnectionPoolManager(){
        pool=new HttpClientConnectionPoolingAdapter();
        pool.setMaxTotal(maxTotal);
        pool.setDefaultMaxPerRoute(defaultMaxPerRoute);
        pool.setDefaultSocketConfig(getCustomSocketConfig());
        buildHttpClient(pool);
    }

    private void buildHttpClient(PoolingHttpClientConnectionManager pool ){
        //HTTP
        httpClientBuilder=HttpClients.custom().setConnectionManager(pool);
        httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(0, false));//重试次数,设置不重试
        httpClientBuilder.setHostnameVerifier(new AllowAllHostnameVerifier()); //忽略域名一致性验证
        //httpClientBuilder.setConnectionReuseStrategy(new HttpConnectionReuseStrategy());
        httpClientBuilder.setKeepAliveStrategy(new HttpConnectionKeepAliveStrategy());
        //httpClientBuilder.setDefaultRequestConfig(getCustomRequestConfig());
        httpClient=httpClientBuilder.build();
        
        
        //HTTPS
        httpsClientBuilder=HttpClients.custom().setConnectionManager(pool);
        httpsClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(0, false));//重试次数,设置不重试
        httpsClientBuilder.setHostnameVerifier(new AllowAllHostnameVerifier()); //忽略域名一致性验证
        //httpsClientBuilder.setConnectionReuseStrategy(new HttpConnectionReuseStrategy());
        httpsClientBuilder.setKeepAliveStrategy(new HttpConnectionKeepAliveStrategy());
        //httpsClientBuilder.setDefaultRequestConfig(getCustomRequestConfig());
        try {
            SSLContext sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, new TrustManager[] {new TrustAllCertManager()}, null);
            httpsClientBuilder.setSslcontext(sslContext);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        httpsClient=httpsClientBuilder.build();
    }
    

    
    /**
     *  获取http client池管理器
     * @return
     */
    public synchronized static HttpClientConnectionPoolManager getInstance(){
        if(poolManager == null){
            poolManager = new HttpClientConnectionPoolManager();
        }
        return poolManager;
    }
    
    /**
     * 获取http client池管理器
     * @param maxTotal 连接池最大连接数
     * @param defaultMaxPerRoute 每个路由最大连接数
     * @return
     */
    public synchronized static HttpClientConnectionPoolManager getInstance(int maxTotal, int defaultMaxPerRoute){
        if(poolManager == null){
            poolManager = new HttpClientConnectionPoolManager(maxTotal, defaultMaxPerRoute);
        }
        return poolManager;
    }

    /**
     * 获取http client
     * @return
     */
    public CloseableHttpClient getHttpClient(){
        if(poolManager == null){
            getInstance();
        }
        return httpClient;
    }
    
    /**
     * 获取仅服务端单向认证的https client
     * @return
     * @throws NoSuchAlgorithmException
     * @throws KeyManagementException
     */
    public CloseableHttpClient getCommonHttpsClient() throws NoSuchAlgorithmException, KeyManagementException{
        if(poolManager == null){
            getInstance();
        }
        return httpsClient;
    }
    
    /**
     * 获取双向认证的https client
     * @param certStoreType
     * @param certStorePath
     * @param certStorePassword
     * @return
     * @throws NoSuchAlgorithmException
     * @throws KeyManagementException
     * @throws KeyStoreException
     * @throws CertificateException
     * @throws IOException
     * @throws UnrecoverableKeyException
     */
    public CloseableHttpClient getStrictHttpsClient(String certStoreType, String certStorePath, String certStorePassword) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, CertificateException, IOException, UnrecoverableKeyException{
        if(poolManager == null){
            getInstance();
        }
        
        // KeyStore keyStore = KeyStore.getInstance("jks");
        // KeyStore keyStore = KeyStore.getInstance("pkcs12");
        KeyStore keyStore = KeyStore.getInstance(certStoreType);
        InputStream in = new FileInputStream(certStorePath);
        keyStore.load(in, certStorePassword.toCharArray());

        KeyManagerFactory kmf = KeyManagerFactory.getInstance("sunx509");
        kmf.init(keyStore, certStorePassword.toCharArray());

        TrustManagerFactory tmf = TrustManagerFactory.getInstance("sunx509");
        tmf.init(keyStore);

        SSLContext sslContext = SSLContext.getInstance("SSL");
        sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
        httpsClientBuilder.setSslcontext(sslContext);
        return httpsClientBuilder.build();
    }
    
    public void closeExpiredConnections(){
        if(pool!=null){
            pool.closeExpiredConnections();            
        }
    }
    
    public void close(){
        if(pool!=null){
            pool.close();            
        }
    }
    
    public void closeIdleConnections(int idleTimeout, TimeUnit timeUnit) {
        if(pool!=null){
            pool.closeIdleConnections(idleTimeout, timeUnit);            
        }
    }
}
